package storage

import (
	"fmt"
	"io"
	"sync"

	"github.com/sirupsen/logrus"
	"github.com/xuelang-group/suanpan-appconnector/pkg/common"
	"github.com/xuelang-group/suanpan-appconnector/pkg/config"
)

var _oss Storage
var storageOnce sync.Once

type StorageParemeter map[string]string

type Storage interface {
	FGetObject(objectName, filePath string, param ...StorageParemeter) error
	FPutObject(objectName, filePath string, param ...StorageParemeter) error
	PutObject(objectName string, reader io.Reader, param ...StorageParemeter) error
	ListObjects(objectPrefix string, recursive bool, maxKeys int, param ...StorageParemeter) ([]ObjectItem, error)
	DeleteObject(objectName string, param ...StorageParemeter) error
	DeleteMultiObjects(objectNames []string, param ...StorageParemeter) error
}

type EnvStorage struct {
	StorageType string `mapstructure:"--storage-type" default:"minio"`
}

type StorageError struct {
	EndpointType string
	Endpoint     string
	Err          string
}

const (
	Minio   = "minio"
	Oss     = "oss"
	Suanpan = "suanpan"
)

func newStorageInstanceFromEnv() (Storage, error) {
	env := config.GetEnv()
	logrus.Debugf("build storage instance, type %s", env.OssType)
	switch env.OssType {
	case Minio:
		var minioStorage MinioStorage = MinioStorage{
			//StorageMinioEndpoint: config.Oss.Endpoint,
			StorageMinioEndpoint:   env.OssEndpoint,
			StorageMinioBucketName: env.OssBucket,
			StorageMinioAccessKey:  env.OssAccessKey,
			StorageMinioSecretKey:  env.OssAccessSecret,
		}
		return &minioStorage, nil
	case Oss:
		var ossStorage OssStorage = OssStorage{
			StorageOssEndpoint:   env.OssEndpoint,
			StorageOssBucketName: env.OssBucket,
			StorageOssAccessId:   env.OssAccessKey,
			StorageOssAccessKey:  env.OssAccessSecret,
			UseStsToken:          false,
		}
		return &ossStorage, nil
	default:
		//connector.PanicError(fmt.Errorf("oss type not supported : %s", env.OssType))
		return nil, (fmt.Errorf("oss type not supported : %s", env.OssType))
	}
}

func NewStorageInstance() (Storage, error) {
	return newStorageInstanceFromEnv()
}

var _ = newStorageInstanceFromArgs

func newStorageInstanceFromArgs() (Storage, error) {
	config := config.GetArgs()
	logrus.Debugf("build storage instance, type %s", config.Oss.Type)
	switch config.Oss.Type {
	case Minio:
		var minioStorage MinioStorage = MinioStorage{
			//StorageMinioEndpoint: config.Oss.Endpoint,
			StorageMinioEndpoint:   config.Oss.InternalEndpoint,
			StorageMinioBucketName: config.Oss.Bucket,
			StorageMinioAccessKey:  config.Oss.AccessKey,
			StorageMinioSecretKey:  config.Oss.AccessSecret,
		}
		return &minioStorage, nil
	case Oss:
		var ossStorage OssStorage = OssStorage{
			// StorageOssEndpoint: config.Oss.Endpoint,
			StorageOssEndpoint:   config.Oss.InternalEndpoint,
			StorageOssBucketName: config.Oss.Bucket,
			StorageOssAccessId:   config.Oss.AccessKey,
			StorageOssAccessKey:  config.Oss.AccessSecret,
			UseStsToken:          false,
		}
		return &ossStorage, nil
	case Suanpan:
		var ossStorage OssStorage = OssStorage{
			// StorageOssEndpoint: config.Oss.Endpoint,
			StorageOssEndpoint:   config.Oss.InternalEndpoint,
			StorageOssBucketName: config.Oss.Bucket,
			UseStsToken:          true,
			StorageOssAccessId:   config.Oss.AccessKey,
			StorageOssAccessKey:  config.Oss.AccessSecret,
		}
		return &ossStorage, nil
	default:
		//connector.PanicError(fmt.Errorf("oss type not supported : %s", config.Oss.Type))
		return nil, (fmt.Errorf("oss type not supported : %s", config.Oss.Type))
	}
}

func (e *StorageError) Error() string {
	//if e == nil {
	//	return "<nil>"
	//}
	s := "oss error on " + e.EndpointType + "server: " + e.Endpoint
	s += "--" + e.Err
	return s
}

func GetOssInstance() Storage {
	var err error
	storageOnce.Do(func() {
		_oss, err = NewStorageInstance()
		if err != nil {
			common.PanicError(err)
		}
	})
	return _oss
}
